package com.ruyuan.commodity.common.mamager.impl;

import cn.hutool.json.JSONUtil;
import com.ruyuan.commodity.builder.GoodsInfoBuilder;
import com.ruyuan.commodity.common.mamager.*;
import com.ruyuan.commodity.common.mapper.GoodsMapper;
import com.ruyuan.commodity.common.mapper.GoodsSkuMapper;
import com.ruyuan.commodity.config.ThreadContextHolder;
import com.ruyuan.commodity.enums.*;
import com.ruyuan.commodity.exception.ServiceException;
import com.ruyuan.commodity.model.goods.dos.GoodsDO;
import com.ruyuan.commodity.model.goods.dos.GoodsSkuDO;
import com.ruyuan.commodity.model.goods.request.GoodsInfoRequest;
import com.ruyuan.commodity.model.goods.vo.BackendGoodsVO;
import com.ruyuan.commodity.model.goods.vo.GoodsVO;
import com.ruyuan.commodity.model.goods.vo.OperateAllowable;
import com.ruyuan.commodity.mq.MqMessageSenderFactory;
import com.ruyuan.commodity.open.api.aspect.UserInfo;
import com.ruyuan.commodity.open.api.client.exchange.api.ExchangeGoodsOpenApi;
import com.ruyuan.commodity.open.api.client.shop.api.ShopOpenApi;
import com.ruyuan.commodity.open.api.model.base.CachePrefix;
import com.ruyuan.commodity.open.api.model.errorcode.GoodsErrorCode;
import com.ruyuan.commodity.open.api.model.errorcode.PromotionErrorCode;
import com.ruyuan.commodity.open.api.model.shop.vo.ShopVO;
import com.ruyuan.commodity.open.api.model.util.DateUtil;
import com.ruyuan.commodity.open.api.util.AuthContextUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import javax.servlet.http.HttpSession;
import java.util.*;

/**
 * 商品业务类
 */
@Service
public class GoodsManagerImpl implements GoodsManager {

    private final Logger logger = LoggerFactory.getLogger(getClass());
    /**
     * 商品相册管理的业务类
     */
    @Autowired
    private GoodsInfoGalleryManager goodsInfoGalleryManager;
    /**
     * 商品属性/参数管理的业务组件
     */
    @Autowired
    private GoodsParamsManager goodsParamsManager;
    /**
     * 商品sku管理的业务组件
     */
    @Autowired
    private GoodsSkuManager goodsSkuManager;
    /**
     * 缓存操作对象
     */
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    /**
     * 商品查询的业务类
     */
    @Autowired
    private GoodsInfoQueryManager goodsInfoQueryManager;
    /**
     * 商品的Open API
     */
    @Autowired
    private ExchangeGoodsOpenApi exchangeGoodsOpenApi;
    /**
     * 店铺的client组件
     */
    @Autowired
    private ShopOpenApi shopOpenApi;
    /**
     * 商品的mapper组件
     */
    @Autowired
    private GoodsMapper goodsMapper;
    /**
     * 商品SKU的Mapper组件
     */
    @Autowired
    private GoodsSkuMapper goodsSkuMapper;
    /**
     * MQ消息组装并发送的工厂
     */
    @Autowired
    private MqMessageSenderFactory mqMessageSenderFactory;

    @Autowired
    private GoodsInfoBuilder goodsInfoBuilder;
    /**
     * Redisson Client
     */
    @Autowired
    private RedissonClient redissonClient;

    /**
     * 添加商品
     *
     * @param request 商品对象
     * @return 商品对象
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public GoodsVO add(GoodsInfoRequest request) {
        // 构造商品信息
        GoodsDO goods = goodsInfoBuilder
                                .buildGoodsBaseInfo(request)
                                .buildGoodsImgInfo(request)
                                .create();
        // 添加商品
        this.goodsMapper.insert(goods);
        // 添加商品属性
        this.goodsParamsManager.addParams(request.getGoodsParamsList(), goods.getGoodsId());
        // 添加商品sku信息，添加时先添加空的sku信息
        this.goodsSkuManager.add(null, goods);
        // 添加相册
        this.goodsInfoGalleryManager.add(request.getGoodsGalleryList(), goods.getGoodsId());

        // 发送增加商品消息 进行商品属性的索引构建（新增） 商品页面静态化等逻辑
        mqMessageSenderFactory.createAndSendMsg(new Long[]{goods.getGoodsId()}, MsgTypeEnums.GOODS_CHANGE_ADD_MSG,
                false, false, "");

        return createGoodsVO(goods);
    }

    /**
     * 修改商品
     *
     * @param request 商品请求入参
     * @param id      商品主键
     * @return Goods 商品
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public GoodsVO edit(GoodsInfoRequest request, Long id) {

        String lockKey = RedissonLockEnums.GOODS_DETAIL_LOCK_PREFIX.getKey() + id;
        RLock lock = redissonClient.getLock(lockKey);
        lock.lock();
        try {
            GoodsDO goodsDO = goodsInfoQueryManager.getModel(id);
            if (goodsDO == null) {
                throw new ServiceException(GoodsErrorCode.E301.code(), "未找到相关商品");
            }
            request.setGoodsId(id);
            GoodsDO goods = goodsInfoBuilder.buildGoodsBaseInfo(request)
                    .buildEditGoodsImgInfo(request)
                    .buildGoodsSpecs(goodsDO)
                    .create();
            // 更新商品
            goods.setGoodsId(id);
            UpdateWrapper<GoodsDO> wrapper = new UpdateWrapper<GoodsDO>();
            wrapper.eq("goods_id", id);
            // 如果商品品牌为空，更新商品品牌为空
            if (goods.getBrandId() == null) {
                wrapper.set("brand_id", null);
            }
            goodsMapper.update(goods, wrapper);
            // 处理商品属性信息
            this.goodsParamsManager.addParams(request.getGoodsParamsList(), id);
            // 更新商品规格信息
            this.goodsSkuManager.updateSkuByGoods(goods);
            // 清除该商品关联的缓存数据
            this.cleanGoodsAssociated(id, request.getMarketEnable());
            // 发送更新商品的消息 更新商品文档
            mqMessageSenderFactory.createAndSendMsg(new Long[]{id}, MsgTypeEnums.GOODS_CHANGE_UPDATE_MSG,
                    true, !goodsDO.getGoodsName().equals(request.getGoodsName()), "");
            return createGoodsVO(goods);
        } finally {
            lock.unlock();
        }
    }

    /**
     * 商品下架
     *
     * @param goodsIds   商品id数组
     * @param reason     下架理由
     * @param permission 权限
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void under(Long[] goodsIds, String reason, PermissionEnums permission) {
        UserInfo userInfo = AuthContextUtil.getUserInfo();
        if (reason.length() > 500) {
            throw new ServiceException(PromotionErrorCode.E400.code(), "下架原因长度不能超过500个字符");
        }

        reason = "店员" + userInfo.getUsername() + "下架，原因为：" + reason;

        UpdateWrapper<GoodsDO> updateWrapper = new UpdateWrapper<GoodsDO>()
                //设置状态为下架
                .set("market_enable", GoodsMarketEnableEnums.MARKET_ENABLE_DOWN.getCode())
                //设置下架原因
                .set("under_message", reason)
                //设置修改时间
                .set("last_modify", DateUtil.getDateline())
                .in("goods_id", Arrays.asList(goodsIds));
        goodsMapper.update(new GoodsDO(), updateWrapper);

        // 清除商品相关的缓存数据
        for (long goodsId : goodsIds) {
            this.cleanGoodsAssociated(goodsId, GoodsMarketEnableEnums.MARKET_ENABLE_DOWN.getCode());
        }

        // 发送商品变化的消息
        mqMessageSenderFactory.createAndSendMsg(goodsIds, MsgTypeEnums.GOODS_CHANGE_UNDER_MSG,
                false, false, reason);
    }

    /**
     * 清除商品的关联<br/>
     * 在商品删除、下架要进行调用
     *
     * @param goodsId    商品id
     * @param markEnable 上架状态
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void cleanGoodsAssociated(long goodsId, Integer markEnable) {

        logger.info("清除goodsId为 [{}] 相关的缓存，包括促销的缓存", goodsId);
        // 清除商品的缓存
        redisTemplate.delete(CachePrefix.GOODS.getPrefix() + goodsId);
        // 清除商品详情的缓存
        redisTemplate.delete(RedisCacheKey.GOODS_DETAIL_KEY_PREFIX.getPrefix() + goodsId);

        // 删除这个商品的sku缓存(必须要在删除库中sku前先删缓存),首先查出商品对应的sku_id
        List<GoodsSkuDO> skuIds = goodsSkuMapper.selectList(
                new QueryWrapper<GoodsSkuDO>()
                        .select("sku_id")
                        .eq("goods_id", goodsId));

        for (GoodsSkuDO map : skuIds) {
            // 清除商品详情的缓存
            redisTemplate.delete(CachePrefix.SKU.getPrefix() + map.getSkuId());
        }

        redisTemplate.delete(CachePrefix.GOODS.getPrefix() + goodsId);

        // 删除该商品关联的活动缓存
        long currTime = DateUtil.getDateline();
        String currDate = DateUtil.toString(currTime, "yyyyMMdd");

        // 清除此商品的缓存
        redisTemplate.delete(CachePrefix.PROMOTION_KEY + currDate + goodsId);

        // 删除积分商品
        if (markEnable == 0) {
            this.deleteExchange(goodsId);
        }

    }

    /**
     * 删除积分商品
     *
     * @param goodsId 商品id
     */
    private void deleteExchange(Long goodsId) {
        // 删除积分商品
        exchangeGoodsOpenApi.del(goodsId);
    }

    /**
     * 商品放入回收站
     *
     * @param goodsIds 商品id数组
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void inRecycle(Long[] goodsIds) {

        this.goodsMapper.update(new GoodsDO(),
                new UpdateWrapper<GoodsDO>()
                        // 下架状态
                        .set("market_enable", GoodsMarketEnableEnums.MARKET_ENABLE_DOWN.getCode())
                        // 删除
                        .set("disabled", GoodsDisableStatusEnums.DEL.getCode())
                        // 修改最后修改时间
                        .set("last_modify", DateUtil.getDateline())
                        .in("goods_id", Arrays.asList(goodsIds)));

        //清除相关的关联
        for (long goodsId : goodsIds) {
            this.cleanGoodsAssociated(goodsId, GoodsMarketEnableEnums.MARKET_ENABLE_DOWN.getCode());
        }

        //发送商品变化的消息
        mqMessageSenderFactory.createAndSendMsg(goodsIds, MsgTypeEnums.GOODS_CHANGE_IN_RECYCLE_MSG,
                false, false, "");

    }

    /**
     * 商品删除
     *
     * @param goodsIds 商品id数组
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delete(Long[] goodsIds) {

        this.goodsMapper.update(new GoodsDO(),
                new UpdateWrapper<GoodsDO>()
                        // 修改删除商品的逻辑不做物理删除
                        .set("disabled", -1)
                        .in("goods_id", Arrays.asList(goodsIds)));
        // 除商品发送商品删除消息
        mqMessageSenderFactory.createAndSendMsg(goodsIds, MsgTypeEnums.GOODS_CHANGE_DELETE_MSG,
                false, false, "");
    }

    /**
     * 回收站还原商品
     *
     * @param goodsIds 商品id数组
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void revert(Long[] goodsIds) {
        this.goodsMapper.update(new GoodsDO(),
                new UpdateWrapper<GoodsDO>()
                        //更新删除状态
                        .set("disabled", 1)
                        .in("goods_id", Arrays.asList(goodsIds)));

        // 发送商品变化消息
        mqMessageSenderFactory.createAndSendMsg(goodsIds, MsgTypeEnums.GOODS_CHANGE_REVERT_MSG,
                false, false, "");

    }

    /**
     * 上架商品
     *
     * @param goodsId 商品id
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void up(Long goodsId) {
        // 查看是否允许上架
        GoodsDO goods = this.goodsMapper.selectOne(
                new QueryWrapper<GoodsDO>()
                        .select("disabled,market_enable,seller_id")
                        .eq("goods_id", goodsId));

        Integer disabled = goods.getDisabled();
        Integer marketEnable = goods.getMarketEnable();
        // 商品所属店铺id
        Long sellerId = goods.getSellerId();
        // 查询店铺是否是关闭中，若未开启，则不能上架
        ShopVO shop = shopOpenApi.getShop(sellerId);
        if (shop == null || !"OPEN".equals(shop.getShopDisable())) {
            throw new ServiceException(GoodsErrorCode.E301.code(), "店铺关闭中,商品不能上架操作");
        }

        // 查看商品是否可以上架
        OperateAllowable operateAllowable = new OperateAllowable(marketEnable, disabled);
        if (!operateAllowable.getAllowMarket()) {
            throw new ServiceException(GoodsErrorCode.E301.code(), "商品不能上架操作");
        }

        this.goodsMapper.update(new GoodsDO(),
                new UpdateWrapper<GoodsDO>()
                        //设置状态为上架
                        .set("market_enable", 1)
                        //设置商品为未删除
                        .set("disabled", 1)
                        .eq("goods_id", goodsId));

        // 移除商品缓存
        redisTemplate.delete(CachePrefix.GOODS.getPrefix() + goodsId);

        // 发送商品变化消息
        mqMessageSenderFactory.createAndSendMsg(new Long[]{goodsId}, MsgTypeEnums.GOODS_CHANGE_UPDATE_MSG,
                false, false, "");

    }

    /**
     * 增加商品的浏览次数
     *
     * @param goodsId 商品id
     * @return 商品浏览次数
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Integer visitedGoodsNum(Long goodsId) {
        /**
         * 逻辑：判断当前缓存中的viewedGoods变量是否包含了当前goods，如未，则 向变量中加入goods，如果已经存在则
         * 1、从redis中取出此商品的浏览数 2、如果为空则新增此商品浏览数为1，否则判断浏览数是否大于等于50，逻辑为每50次存入数据库 3、存入相关数据库
         */
        HttpSession session = ThreadContextHolder.getHttpRequest().getSession();
        String visitCountStr = redisTemplate.opsForValue().get(CachePrefix.VISIT_COUNT.getPrefix() + session.getId());
        List<Long> visitedGoods = JSONUtil.toList(visitCountStr, Long.class);
        // 标识此会话是否访问过
        boolean visited = false;
        if (visitedGoods == null) {
            visitedGoods = new ArrayList<Long>();
        }

        if (visitedGoods.contains(goodsId)) {
            visited = true;
            visitedGoods.remove(goodsId);
        }
        visitedGoods.add(0, goodsId);

        String key = CachePrefix.VISIT_COUNT.getPrefix() + goodsId;
        // 获取redis中此商品的浏览数
        String goodsViewCountStr = redisTemplate.opsForValue().get(key);
        Integer num = Integer.valueOf(StringUtils.isEmpty(goodsViewCountStr) ? "0" : goodsViewCountStr);
        if (!visited) {

            num++;
            // 如果浏览数大于等于50则存入相关数据库
            if (num >= 50) {
                // num为历史访问量，如果满足条件需要将此次浏览数也要加进去 固+1
                this.goodsMapper.update(new GoodsDO(),
                        new UpdateWrapper<GoodsDO>()
                                .setSql("view_count = view_count +" + num)
                                .eq("goods_id", goodsId));
                num = 0;
            }
            redisTemplate.opsForValue().set(key, String.valueOf(num));
        }

        return num;
    }

    /**
     * 获取新增商品
     *
     * @param length 长度
     * @return 商品列表
     */
    @Override
    public List<BackendGoodsVO> newGoods(Integer length) {
        return this.goodsMapper.newGoods(length);
    }

    /**
     * 获取商品是否使用检测的模版
     *
     * @param templateId 运费模板id
     * @return 商品
     */
    @Override
    public GoodsDO checkShipTemplate(Long templateId) {

        List<GoodsDO> goodsDOS = this.goodsMapper.selectList(
                new QueryWrapper<GoodsDO>()
                        .ne("disabled", 1)
                        .eq("template_id", templateId));

        if (goodsDOS != null && goodsDOS.size() > 0) {
            return goodsDOS.get(0);
        }
        return null;
    }

    /**
     * 获取店铺的商品
     *
     * @param sellerId 商家id
     * @return 商品列表
     */
    private List<GoodsDO> getAll(Long sellerId) {

        List<GoodsDO> goodsList = this.goodsMapper.selectList(
                new QueryWrapper<GoodsDO>()
                        .eq("seller_id", sellerId));

        return goodsList;
    }

    /**
     * 获取店铺的sku
     *
     * @param sellerId 商家id
     * @return sku列表
     */
    private List<GoodsSkuDO> getAllSku(Long sellerId) {
        List<GoodsSkuDO> goodsList = this.goodsSkuMapper.selectList(
                new QueryWrapper<GoodsSkuDO>()
                        .eq("seller_id", sellerId));
        return goodsList;
    }

    /**
     * 更改商品类型
     *
     * @param sellerId 商家id
     * @param type     类型
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateGoodsType(Long sellerId, String type) {
        this.goodsMapper.update(new GoodsDO(),
                new UpdateWrapper<GoodsDO>()
                        //修改商品类型
                        .set("goods_type", type)
                        //根据商家id修改
                        .eq("seller_id", sellerId));
    }

    /**
     * 创建商品VO
     * @param goods 商品DO类
     * @return 商品VO
     */
    private GoodsVO createGoodsVO(GoodsDO goods) {
        GoodsVO goodsVO = new GoodsVO();
        goodsVO.setGoodsId(goods.getGoodsId());
        goodsVO.setCategoryId(goods.getCategoryId());

        return goodsVO;
    }

}

